module top(
	input clk,
	input resetn,
	output trap,
	output reg [7:0] out_byte,
	output reg out_byte_en,
    output clk_o,
    output mem_valid_o,
    output mem_ready_o,
    output led,
    output uart_tx,
    input uart_rx,

    output       tmds_clk_n,
    output       tmds_clk_p,
    output [2:0] tmds_d_n,
    output [2:0] tmds_d_p,

    output psram_clk,
    inout [3:0] psram_dio,
    output psram_cs,

    output spi_clk,
    output spi_mosi,
    input spi_miso,

    output uart2_tx,
    input uart2_rx,
    input dbg_uart_rx,
	output dbg_uart_tx,
    input [3:0] key//
);
wire sys_resetn;

wire mem_valid;
wire mem_instr;
wire [31:0] mem_addr;
wire [31:0] mem_wdata;
wire [3:0] mem_wstrb;

wire mem_ready;
wire [31:0] mem_rdata;

assign mem_valid_o = 1'b1;
assign mem_ready_o = mem_ready;

wire [31:0] riscv_pc;
wire cpu_reset;


wire clk_p;
wire clk_p5;
wire pll_lock;

Gowin_rPLL u_pll (
  .clkin(clk),
  .clkout(clk_p5),
//  .clkoutd(clk_p),
  .lock(pll_lock)
);

Gowin_CLKDIV u_div_5 (
    .clkout(clk_p),
    .hclkin(clk_p5),
    .resetn(pll_lock)
);
Reset_Sync u_Reset_Sync (
  .resetn(sys_resetn),
  .ext_reset(resetn & pll_lock),
  .clk(clk_p)
);
riscv32 #(
		.STACKADDR(32'hffff_fff0),
        .CLK_FREQ	(25000000),
	    .UART_BPS	(115200)
	)picorv32_core(
	.clk      (clk_p     ),
    .resetn   (sys_resetn ),
    .trap     (  trap   ),
    .mem_valid(mem_valid),
    .mem_instr(mem_instr),
    .mem_ready(mem_ready),
    .mem_addr (mem_addr ),
    .mem_wdata(mem_wdata),
    .mem_wstrb(mem_wstrb),
    .mem_rdata(mem_rdata),
    .tlb_cached(!out_byte[7]),
    .dbg_uart_rx(dbg_uart_rx),
    .dbg_uart_tx(dbg_uart_tx),
    .PC(riscv_pc),
    .irq({30'd0,~{key[1]},1'b0}),
    .cpu_reset_out(cpu_reset)
);

reg mem_ready_r0;
wire mem_ready_r1;
reg [31:0] mem_rdata_r0;
wire [31:0] mem_rdata_r1;



wire [31:0] spi_rdata;
wire spi_ready;
wire spi_valid;

assign mem_ready = (mem_addr[31:16] == 16'h0) ? mem_ready_r1 : 
                (mem_addr >= 32'h1000_0004)&&(mem_addr < 32'h1000_000c) ? spi_ready : mem_ready_r0;
assign mem_rdata = (mem_addr[31:16] == 16'h0) ? mem_rdata_r1 :
                (mem_addr >= 32'h1000_0004)&&(mem_addr < 32'h1000_000c) ? spi_rdata: mem_rdata_r0;

uart_memory#(
	.CLK_FREQ	(25000000),
	.UART_BPS	(115200)
) uart_memory_u1(
    .clk(clk_p),
    .resetn(sys_resetn),
    .uart_rx(uart_rx),
    .uart_tx(uart_tx),
    .memory_en(1'b1),
    .mem_valid(mem_valid),
    .mem_instr(mem_instr),
    .mem_addr(mem_addr),
    .mem_wdata(mem_wdata),
    .mem_wstrb(mem_wstrb),
    .mem_rdata(mem_rdata_r1),
    .mem_ready(mem_ready_r1)
);
assign spi_valid = (mem_addr >= 32'h1000_0004)&&(mem_addr < 32'h1000_000c);
spi spi_u0(
    .clk(clk_p),
    .resetn(sys_resetn),

    .mem_valid(mem_valid),
    .mem_rdata(spi_rdata),
    .mem_wdata(mem_wdata),
    .mem_ready(spi_ready),
    .mem_wstrb(mem_wstrb),
    .mem_addr(mem_addr),

    .spi_clk(spi_clk),
    .spi_mosi(spi_mosi),
    .spi_miso(spi_miso)
);

assign psram_valid = (mem_addr >= 32'h80_0000)&&(mem_addr < 32'h100_0000) && 
                    ((mem_valid == 1'b1)||(mem_instr == 1'b1));

wire [3:0] RAM_WE;
assign RAM_WE = ((mem_addr[16:0] > 17'h0ffff)&&(mem_addr[16:0] < 17'h14000)) ? mem_wstrb : 4'h0;
wire [31:0] RAM_RDATA;

RAM RAM_u0(
    .dout(RAM_RDATA[7:0]), //output [7:0] dout
    .clk(clk_p), //input clk
    .oce(1'b1), //input oce
    .ce(1'b1), //input ce
    .reset(~sys_resetn), //input reset
    .wre(RAM_WE[0]), //input wre
    .ad(mem_addr[13:2]), //input [11:0] ad
    .din(mem_wdata[7:0]) //input [7:0] din
);
RAM RAM_u1(
    .dout(RAM_RDATA[15:8]), //output [7:0] dout
    .clk(clk_p), //input clk
    .oce(1'b1), //input oce
    .ce(1'b1), //input ce
    .reset(~sys_resetn), //input reset
    .wre(RAM_WE[1]), //input wre
    .ad(mem_addr[13:2]), //input [11:0] ad
    .din(mem_wdata[15:8]) //input [7:0] din
);
RAM RAM_u2(
    .dout(RAM_RDATA[23:16]), //output [7:0] dout
    .clk(clk_p), //input clk
    .oce(1'b1), //input oce
    .ce(1'b1), //input ce
    .reset(~sys_resetn), //input reset
    .wre(RAM_WE[2]), //input wre
    .ad(mem_addr[13:2]), //input [11:0] ad
    .din(mem_wdata[23:16]) //input [7:0] din
);
RAM RAM_u3(
    .dout(RAM_RDATA[31:24]), //output [7:0] dout
    .clk(clk_p), //input clk
    .oce(1'b1), //input oce
    .ce(1'b1), //input ce
    .reset(~sys_resetn), //input reset
    .wre(RAM_WE[3]), //input wre
    .ad(mem_addr[13:2]), //input [11:0] ad
    .din(mem_wdata[31:24]) //input [7:0] din
);

svo_hdmi_top u_hdmi (
	.clk(clk_p),
	.resetn(sys_resetn),

	// video clocks
	.clk_pixel(clk_p),
	.clk_5x_pixel(clk_p5),
	.locked(pll_lock),

	.term_in_tvalid(  ),
	.term_out_tready(),
	.term_in_tdata( ),

	// output signals
	.tmds_clk_n(tmds_clk_n),
	.tmds_clk_p(tmds_clk_p),
	.tmds_d_n(tmds_d_n),
	.tmds_d_p(tmds_d_p)
);

reg [1:0] ram_cnt;
always @(posedge clk_p or negedge sys_resetn) begin
        if(!sys_resetn)begin
            ram_cnt <= 2'b00;
            out_byte <= 8'h0;
            mem_ready_r0 <= 0;
        end
        else begin
            if(!mem_valid)begin
                ram_cnt <= 2'b00;
                mem_ready_r0 <= 1'b0;
            end
		    
            else if (sys_resetn && mem_valid && !mem_ready) begin

                    if(mem_addr == 32'h1000_0000) begin
                        if(|mem_wstrb) begin
                            out_byte <= mem_wdata[7:0];
                            mem_ready_r0 <= 1'b1;
                        end
                        else begin
                            mem_rdata_r0 <= {24'h0, out_byte[7:0]};
                            mem_ready_r0 <= 1'b1;
                            /*if(ram_cnt == 2'b01)begin
                                mem_rdata_r0 <= {24'h0, out_byte[7:0]};
                                mem_ready_r0 <= 1'b1;
                            end
                            else begin
                                ram_cnt <= ram_cnt + 1'b1;
                                mem_ready_r0 <= mem_ready_r0;
                            end*/
                        end
                    end
                    else if((mem_addr[16:0] > 17'h0ffff)&&(mem_addr[16:0] < 17'h14000)) begin
                        if(|mem_wstrb) begin
                            mem_ready_r0 <= 1'b1;
                        end
                        else begin
                            if(ram_cnt == 2'b01)begin
                                mem_rdata_r0 <= RAM_RDATA;
                                mem_ready_r0 <= 1'b1;
                            end
                            else begin
                                ram_cnt <= ram_cnt + 1'b1;
                                mem_ready_r0 <= mem_ready_r0;
                            end
                        end
                    end
                    else if((mem_addr >= 32'h80_0000)&&(mem_addr < 32'h100_0000)) begin
                        
                    end
                    else begin
                        mem_rdata_r0 <= {24'h0, out_byte[7:0]};
                        mem_ready_r0 <= 1'b1;
                    end
            end
        end
	end
endmodule

module Reset_Sync (
 input clk,
 input ext_reset,
 output resetn
);

 reg [3:0] reset_cnt = 0;
 
 always @(posedge clk or negedge ext_reset) begin
     if (~ext_reset)
         reset_cnt <= 4'b0;
     else
         reset_cnt <= reset_cnt + !resetn;
 end
 
 assign resetn = &reset_cnt;

endmodule